/***************************************************************************
 *   Copyright (C) 2007 by Anders Nore									   								 *
 *   andenore@start.no   												   												 *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#include <netinet/in.h>

#include "unp.h"
#include "mcastchat.h"
#include "duxinterface.h"
#include "extra.h"

/* Our different commands 
* e.g:  /send 224.0.0.2 MonkeyLover!
*/
command_t command_quit;		/* our quit function */
command_t command_join;		/* join different multicast groups */
command_t command_leave;	/* leave joined multicast groups */
command_t command_list;		/* list up our joined multicast groups */
command_t command_send;		/* sends message to different multicastgroups */
command_t command_help;		/* prints help message to the user */
command_t command_ignore; /* ignores specific ip-addresses */

/* setup our command switchtable */
struct commandsw commandsw_t[] = {
	{
		"q",
		&command_quit
	},
	{
		"quit",
		&command_quit
	},
	{
		"join",
		&command_join
	},
	{
		"leave",
		&command_leave
	},
	{
		"j",
		&command_join
	},
	{
		"send",
		&command_send
	},
	{
		"list",
		&command_list
	},
	{
		"help",
		&command_help
	}
};
#define switchtable_length \
	(sizeof(commandsw_t) / sizeof(struct commandsw))

/**
* checkInput checks our input from stdin for various commands 
* and calls them, otherwise sends message to default multicastgroup.
* @param inputbuf input textbuffer (standard input)
* @return CMD_NOCOMMAND or CMD_COMMAND
*/
int checkInput(char *inputbuf)
{
	/* First character needs to be a '/' to be considered a commando	*/
	if( inputbuf[0] != '/' )
		return CMD_NOCOMMAND;
	else {
		int i, commandlen;
		char *sep = " ";
		char commandstr[MAX_COMMANDNAME_LEN], *word, *last;
		char args[MAX_ARGS_LEN];
		
		bzero(&commandstr, sizeof(commandstr));
		bzero(&args, sizeof(args));
		
		char *firstspace = strchr(inputbuf, SEPERATOR);	/* 0x20 is ASCII "space" */
		if(firstspace != NULL)
			commandlen = firstspace - &inputbuf[1];	/* calculates length to this character */
		else
			commandlen = strlen(inputbuf) - 1;	/* exclude ending '\0' */
		
		strncpy((char *)&commandstr, (char *)&inputbuf[1], commandlen);
		
		for(i = 0; i < switchtable_length; i++) {
			//DEBUG_PRINT_STR("Check string", &commandstr);
			//DEBUG_PRINT_STR("Command string ", &commandsw_t[i].com_name);
			if( strncmp((char *)&commandstr, (char *)&commandsw_t[i].com_name, MAX_COMMANDNAME_LEN) == 0) {
				/* find possible arguments */
				if( strlen(inputbuf) > (commandlen + 2)) {
					strncpy((char *)&args, &inputbuf[commandlen + 2], strlen(inputbuf) - (commandlen + 1));	
				}
// 				DEBUG_PRINT_STR("Args string:", &args);
				
				/* execute command */
				setColor(mainwin, COLOR_COMMAND);
				(*commandsw_t[i].com_func)(&args);
				setColor(mainwin, COLOR_DEFAULT);
				wrefresh(mainwin);
				
				/* if we find our command execute and stop searching(break) */
				break;
			}
		}
		/* If we traversed the whole list and nothing matches */
		if(i == switchtable_length)
			SYSPRINT("Unknown command");
	}
	return CMD_COMMAND;
}

/**
* Quits the program
*/
int command_quit( void *args )
{
	SYSPRINT("Welcome back");
	exit(0);
}

/**
* Join a multicast group
* @param args a string representing a multicastgroups. 
* E.g. "/join 224.0.0.2 224.0.0.3"
*/
int command_join( void *args ) 
{
	if(num_joined_groups < MAX_GROUP_JOINS) {
		int i;
		char *joinstring, **mcastgroups;
		joinstring = (char *)args;
		in_addr_t inaddr;
		struct sockaddr_in grpaddr;
		
		int numjoins = cutString(&joinstring, " ", &mcastgroups);
		
		for( i=0; i<numjoins; i++) {
			if( ((inaddr = inet_addr(mcastgroups[i])) == INADDR_NONE) || !IN_MULTICAST(ntohl(inaddr)) ) {
				SYSPRINT("Not a valid multicastgroup to join: %s", mcastgroups[i]);
				continue;
			}
			
			bzero(&grpaddr, sizeof(grpaddr));
			grpaddr.sin_family = AF_INET;
			grpaddr.sin_addr.s_addr = inaddr;
			
			if(!ingroup((SA*) &grpaddr)) {
				Mcast_join(sockfd, (SA *)&grpaddr, sizeof(grpaddr), NULL, str_interface_name);
				SYSPRINT("Joined multicastgroup: %s", mcastgroups[i]);
			} else
				SYSPRINT("Already joined multicastgroup: %s", mcastgroups[i]);
		}
			
		freeCutString(mcastgroups);
		return 0;
	
	} else {
		SYSPRINT("Sorry, reached maximum number of group joins");
		return -1;
	}
}

/**
* command_leave  leaves a previously joined multicastgroup
* E.g.  "/leave 224.0.0.2 224.0.0.3"
*/
int command_leave( void *args )
{
	int i, j;	
	struct sockaddr_in grp;
	char *leavestring;
	leavestring = (char *)args;
	
	char **multicastgroups;
	int numleaves = cutString(&leavestring, " ", &multicastgroups);
	
	for(i=0; i<numleaves; i++) {		
		bzero(&grp, sizeof(grp));
		grp.sin_family = AF_INET;
		if( (Sock_pton(multicastgroups[i], &grp.sin_addr.s_addr) == -1) ||
					 !IN_MULTICAST(ntohl(grp.sin_addr.s_addr)) ) {
						SYSPRINT("Not a valid multicast address to leave: %s", multicastgroups[i]);
						continue;
		}
		
		if(ingroup((SA*) &grp)) {
			SYSPRINT("Leaving multicastgroup: %s", multicastgroups[i]);
			Mcast_leave(sockfd, (SA*)&grp, sizeof(grp), NULL, str_interface_name);
		} else {
			SYSPRINT("Could not leave: %s, maybe it's not joined", multicastgroups[i]);
		}
	}
	
	freeCutString(multicastgroups);
	
	return 0;
}

/**
* Sends a message to the given multicastgroup
* E.g: "/send 224.0.0.2 Monkeylovers"  sends the string Monkeylovers to
* multicastgroup 224.0.0.2
*/
int command_send( void *args )
{
	char *sendstring;
	sendstring = (char *)args;
	
	char **mcastgroup;
	extractStringAtPos(0, " ", &sendstring, &mcastgroup);
	if(strlen(sendstring) == 0) {
		SYSPRINT("Cannot send empty string");
		return -1;
	}
	
	in_addr_t inaddr;
	if( (inaddr = inet_addr(mcastgroup)) == INADDR_NONE) {
		SYSPRINT("Not a valid address: %s, \"/send <multicastaddress> <string>\" ", mcastgroup);
		return -1;
	}
	
	struct sockaddr_in grpaddr;
	bzero(&grpaddr, sizeof(grpaddr));
	grpaddr.sin_family = AF_INET;
	grpaddr.sin_port = htons(SERV_PORT);
	grpaddr.sin_addr.s_addr = inaddr;

	if( sendto(sockfd, sendstring, strlen(sendstring), (int)NULL, (SA *)&grpaddr, sizeof(grpaddr)) == -1) {
		SYSPRINT("Could not send string to multicastaddress");
		return -1;
	}
	
	return 0;
}

/**
* command_ignore takes in a multicastgroup address and one
* or many ip-addresses and ignores future packets from these on 
* the specified multicastgroup.
*/
/*
int command_ignore( void *args )
{
	int i;
	struct sockaddr_in grp;
	char **addrs;
	char *ignorelist;
	ignorelist = (char *)args;
		
	int numargs = cutString(&ignorelist, " ", &addrs);
	
	bzero(&grp, sizeof(grp));
	grp.sin_family = AF_INET;
	Sock_pton( addrs[0], &grp.sin_addr.s_addr );
	
	for( i=1; i<numargs; i++) {
		Mcast_block_source(sockfd, (SA*)&grp, addrs[i], str_interface_name);
		SYSPRINT("Blocked %s from %s", addrs[i], addrs[0]);
	}
	
	freeCutString(addrs);
	
	
	return 0;
}
*/

/**
* command_list lists the currently joined multicastgroups
*/
int command_list( void *args )
{
	int i;
	struct sockaddr sa;
	
	bzero(&sa, sizeof(sa));
	
	wprintw(mainwin, "Groups Joined %i :\n", num_joined_groups);
		
	for(i = 0; i<MAX_GROUP_JOINS; i++) {
		if( memcmp(&joined_groups[i], &sa, sizeof(sa)) == 0)
			continue;
		
		wprintw(mainwin, "   %s\n", Sock_ntop(&joined_groups[i], sizeof(joined_groups[i])) );
	}
	
	return 0;
}

/**
* command_help lists the various types of commands
*/
int command_help( void *args )
{
	wattron(mainwin, COLOR_PAIR(COLOR_COMMAND));
	wprintw(mainwin, "\nCommands:\n");
	wprintw(mainwin, "/join  <multicast groups>\t joins these multicastgroups\n");
	wprintw(mainwin, "/leave <multicast groups>\t leaves these multicastgroups\n");
	wprintw(mainwin, "/send  <multicastgroup> <string> sends message in string to the multicastgroup\n");
	wprintw(mainwin, "/list\t\t lists the currently joined multicastgroups\n");
	wprintw(mainwin, "/help\t\t prints this information\n");
	wprintw(mainwin, "/q, /quit\t quits this program\n\n");
	wattron(mainwin, COLOR_PAIR(COLOR_DEFAULT));
	
	return 0;
}

